/*
 * Prismatic.cpp
 *
 *  Created on: Jan 2, 2009
 *      Author: Michael
 */

#include "Prismatic.h"

Prismatic::Prismatic(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP)
: D3DApp(hInstance, winCaption, devType, requestedVP)
{}

Prismatic::~Prismatic()
{
}

bool Prismatic::Init()
{
	//make sure the computer has the proper hardware for our settings
	if(!checkDeviceCaps())
	{
		MessageBox(0, "checkDeviceCaps() Failed", 0, 0);
		PostQuitMessage(0);
	}

	////////////////////////////////////////////////////////////////////
	//
	// PLEASE SET UP CAMERA SO THAT X INCREASES TO THE RIGHT AND Y
	//		INCREASES TO THE UP
	//			~Cai 1/4/2008
	//
	////////////////////////////////////////////////////////////////////

	//set up camera info
	//mCameraRadius    = 10.0f;
	mCameraRadius    = 1.0f;
	//mCameraRotationY = 0.5 * D3DX_PI;
	mCameraRotationY = 0.0 * D3DX_PI;
	//mCameraHeight    = 5.0f;
	mCameraHeight    = 15.0f;

	//this creates a new class for the font class (well what will become our font class)
	mGfxStats = new GfxStats();

//	//obtain a pointer to a ID3DXSprite class, mSprite being the pointer
	HR(D3DXCreateSprite(gd3dDevice, &mSprite));

	//font declaration just cuz i didn't want a plain window
	D3DXFONT_DESC fontDesc;
	fontDesc.Height = 80;
	fontDesc.Width = 40;
	fontDesc.Weight = FW_BOLD;
	fontDesc.MipLevels = 0;
	fontDesc.Italic = true;
	fontDesc.CharSet = DEFAULT_CHARSET;
	fontDesc.OutputPrecision = OUT_DEFAULT_PRECIS;
	fontDesc.Quality = DEFAULT_QUALITY;
	fontDesc.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
	_tcscpy(fontDesc.FaceName, _T("Comic Sans"));
	HR(D3DXCreateFontIndirect(gd3dDevice, &fontDesc, &mFont));

	// Adds in Direct Sound
/*	sound.SetWindowHandle(mhMainWnd);
	sound.InitDirectSound();
*/

//	setupFX();

	//we call this because we want the device states to be set during construction as well as when a device is reset
	onResetDevice();

	return true;
}

void Prismatic::CleanUp()
{
	//clean up any states left on the stack
	while(!states.empty())
	{
		states.back()->CleanUp();
		states.pop_back();
	}

	delete mGfxStats;
	//ReleaseCOM(mFX);
	ReleaseCOM(mFont);
	ReleaseCOM(mSprite);
}

bool Prismatic::checkDeviceCaps()
{
	//check for shader support
	D3DCAPS9 caps;
	HR(gd3dDevice->GetDeviceCaps(&caps));

	//check for vertex and pixel shader version 2.0 support
	if(caps.VertexShaderVersion < D3DVS_VERSION(2, 0))
	{
		MessageBox(0, "Your computer does not support Vertex Shader Version 2.0!", "Vertex Shader Support", 0);
		return false;
	}

	if(caps.PixelShaderVersion < D3DPS_VERSION(2, 0))
	{
		MessageBox(0, "Your computer does not support Pixel Shader Version 2.0!", "Pixel Shader Support", 0);
		return false;
	}
	return true;
}

void Prismatic::onLostDevice()
{
	//this is where we put any code that needs to be executed before the rendering device is reset
	mGfxStats->onLostDevice();
	HR(mFont->OnLostDevice());
	gDInput->onLostDevice();
	HR(mSprite->OnLostDevice());

	//added to fix full screen bug MJP 6/2/08
	//HR(mFX->OnLostDevice());
}

void Prismatic::onResetDevice()
{
	// The aspect ratio depends on the backbuffer dimensions, which can
	// possibly change after a reset.  So rebuild the projection matrix.
	//buildProjMtx();

	//call the onResetDevice of other objects
	mGfxStats->onResetDevice();
	HR(mSprite->OnResetDevice());
	HR(mFont->OnResetDevice());

	//sets up camera a 1000 units back looking at the origin
	D3DXMATRIX V;
	D3DXVECTOR3 pos(0.0f, 0.0f, -1000.0f);
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
	D3DXMatrixLookAtLH(&V, &pos, &target, &up);
	HR(gd3dDevice->SetTransform(D3DTS_VIEW, &V));

	//the following cod edefines the volume of space the camera sees
	D3DXMATRIX P;
	RECT R;
	GetClientRect(mhMainWnd, &R);
	float width = (float)R.right;
	float height = (float)R.bottom;
	D3DXMatrixPerspectiveFovLH(&P, D3DX_PI * 0.25f, width/height, 1.0f, 5000.0f);
	HR(gd3dDevice->SetTransform(D3DTS_PROJECTION, &P));

	//this code sets texture fileters, which helps to smooth out distortions when you scale a texture
	HR(gd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR));
	HR(gd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR));
	HR(gd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR));

	//this line of code disables Direct3D Lighting (for now)
	HR(gd3dDevice->SetRenderState(D3DRS_LIGHTING, false));

	//the following code specifies an alpha test and reference value
	HR(gd3dDevice->SetRenderState(D3DRS_ALPHAREF, 10));
	HR(gd3dDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER));

	//the following code is used to set up alpha blending
	HR(gd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE));
	HR(gd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1));
	HR(gd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA));
	HR(gd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA));

	//indicates we are using 2D texture coordinates
	HR(gd3dDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2));
	//added to fix full screen bug MJP 6/2/08
	//HR(mFX->OnResetDevice());
}

void Prismatic::handleInput()
{
	//Get snapshot of input devices
	gDInput->poll();

	states.back()->HandleInput(this);

}

void Prismatic::updateScene(float dt)
{
	states.back()->Update(this);

	//NOT SURE IF WE SHD DO THIS FIRST OR LAST, WILL RESEARCH
	// The camera position/orientation relative to world space can
	// change every frame based on input, so we need to rebuild the
	// view matrix every frame with the latest changes.
	//buildViewMtx();

	///////////////////////////////////////////////////////////////////////////////
	// FOR GAME STATES ASK CAI OR SEE GDD
	///////////////////////////////////////////////////////////////////////////////

	// do GameWorld's update here?

	//if(gDInput->mouseButtonDown(0))
	//{
	//	char * buffer;
	//	MessageBox(gd3dApp->getMainWindowHandle(), "TEST", itoa((int)gDInput->mouseDX(), buffer, 10), 0);
	//}

}

void Prismatic::drawScene()
{

  // Now we can clear just view-port's portion of the buffer to white...
  HR(gd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0));

  HR(gd3dDevice->BeginScene());

  states.back()->Render(this);

  HR(gd3dDevice->EndScene());

  //present the back buffer
  HR(gd3dDevice->Present(0, 0, 0, 0));

}

void Prismatic::setupFX()
{
	mFX = 0;

	ID3DXBuffer* mFXErrors = 0;

	//create the effects object, if there are errors, display them
		//the following functions takes in the device to attach it too (our Direct3DDevice),
		//the name of the file to pull the effects from
		//defines & includes -- this shd be 0 for us
		//any flags (I want to know if we have errors so I set the Debug flag)
		//the next parameter specifies if we are sharing variables in the fx file with other fx files, we
		//aren't so it is 0
		//the last two parameters are the addresses of pointers to the fx object and the error buffer
	HR(D3DXCreateEffectFromFile(gd3dDevice, "Prismatic.fx", 0, 0, D3DXSHADER_DEBUG, 0, &mFX, &mFXErrors));

	if( mFXErrors )
		MessageBox(0, (char*)mFXErrors->GetBufferPointer(), 0, 0);

	//get handles
	mhTech = mFX->GetTechniqueByName("TransformTech");
	mhWVP = mFX->GetParameterByName(0, "gWVP");
}

//the following functions help set up the camera and the world view
void Prismatic::buildViewMtx()
{
	float x = mCameraRadius * cosf(mCameraRotationY);
	float z = mCameraRadius * sinf(mCameraRotationY);
	//D3DXVECTOR3 pos(x, -mCameraHeight, z);	// are we not using the camera at all right now?
	D3DXVECTOR3 pos(0, 0, -mCameraHeight);
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	D3DXMatrixLookAtLH(&mView, &pos, &target, &up);
}

void Prismatic::buildProjMtx()
{
	float w = WIDTH;
	float h = HEIGHT;
	D3DXMatrixPerspectiveFovLH(&mProj, D3DX_PI * 0.25f, w/h, 1.0f, 5000.0f);
}


void Prismatic::PushState(GameState *state)
{
	if(!states.empty())
	{
		states.back()->Pause();
	}

	states.push_back(state);
	states.back()->Init();
}

void Prismatic::PopState(GameState *state)
{
	if(states.empty())
		return;

	//clean up before you remove a state
	states.back()->CleanUp();
	states.pop_back();
	states.back()->Resume();
}

void Prismatic::ChangeState(GameState *state)
{
	if(!states.empty())
	{
		//clean up state
		states.back()->CleanUp();
		states.pop_back();
	}

	//add new state and initialize
	states.push_back(state);
	states.back()->Init();
}

float Prismatic::mouseXCoord()
{
	// this function will return the x coordinate of the mouse
	//	with the game world as a flat plane with z == 0

	D3DXVECTOR3 originW, dirW;
	POINT s;
	GetCursorPos(&s);
	ScreenToClient(mhMainWnd, &s);
	float w = (float)WIDTH;
	//float h = (float)HEIGHT;

	//D3DXMATRIX proj;// = mProj;	// we never built mProj!

	//D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI * 0.25f, w/h, 1.0f, 5000.0f);

	float x = (2.0f*s.x/w - 1.0f);// / proj(0,0);
	//float y = (-2.0f*s.y/h + 1.0f) / proj(1,1);

	//float cameraDist = mCameraHeight;
	//if(cameraDist < 0)
	//{
	//	cameraDist *= -1;
	//}

	//float x2 = x * cameraDist;
	//float y2 = y * abs(cameraDist);

	return x * w * 0.5;

	/*
	///////////// BUILD PICKING RAY /////////////////////////
	D3DXVECTOR3 originW, dirW;
	POINT s;
	GetCursorPos(&s);
	ScreenToClient(wndHandle, &s);
	float w = (float)SCREEN_WIDTH;
	float h = (float)SCREEN_HEIGHT;

	D3DXMATRIX proj = matProj;

	float x = (2.0f*s.x/w - 1.0f) / proj(0,0);
	float y = (-2.0f*s.y/h + 1.0f) / proj(1,1);

	float x2 = x * abs(cameraPosition.z);
	float y2 = y * abs(cameraPosition.z);

	// we now have coordinates on the 2D plane! yay!

	///////////////////////////////////////////////////////////////////
	*/
}

float Prismatic::mouseYCoord()
{
	// this function will return the y coordinate of the mouse
	//	with the game world as a flat plane with z == 0

	D3DXVECTOR3 originW, dirW;
	POINT s;
	GetCursorPos(&s);
	ScreenToClient(mhMainWnd, &s);
	//float w = (float)WIDTH;
	float h = (float)HEIGHT;

	//D3DXMATRIX proj = mProj;

	//float x = (2.0f*s.x/w - 1.0f) / proj(0,0);
	float y = (-2.0f*s.y/h + 1.0f);// / proj(1,1);

	//float x2 = x * abs(mCameraHeight);
	//float y2 = y * abs(mCameraHeight);

	return y * h * 0.5;
}
